package http3

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"sync"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"github.com/quic-go/quic-go/http3"
)

var (
	http3Metrics = promauto.NewGaugeVec(
		prometheus.GaugeOpts{
			Name: "http3_benchmark_metrics",
			Help: "HTTP/3 benchmark metrics",
		},
		[]string{"metric"},
	)
)

type BenchmarkResult struct {
	TotalRequests      int64
	SuccessfulRequests int64
	FailedRequests     int64
	TotalLatency       time.Duration
	MinLatency         time.Duration
	MaxLatency         time.Duration
	PacketLossRate     float64
	Throughput         float64 // MB/s
	Duration           time.Duration
}

type BenchmarkConfig struct {
	URL            string
	Connections    int
	RequestsPerCon int
	Duration       time.Duration
	PayloadSize    int
}

func RunBenchmark(config BenchmarkConfig) (*BenchmarkResult, error) {
	result := &BenchmarkResult{
		MinLatency: time.Hour, // 初始设为一个较大值
	}

	client := &http.Client{
		Transport: &http3.Transport{},
	}

	var wg sync.WaitGroup
	start := time.Now()

	for i := 0; i < config.Connections; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()

			for j := 0; j < config.RequestsPerCon; j++ {
				reqStart := time.Now()

				req, _ := http.NewRequestWithContext(
					context.Background(),
					"GET",
					config.URL,
					nil,
				)

				resp, err := client.Do(req)
				if err != nil {
					result.FailedRequests++
					continue
				}

				io.Copy(io.Discard, resp.Body)
				resp.Body.Close()

				latency := time.Since(reqStart)

				// 更新结果
				result.TotalRequests++
				result.SuccessfulRequests++
				result.TotalLatency += latency

				// 更新最小/最大延迟
				if latency < result.MinLatency {
					result.MinLatency = latency
				}
				if latency > result.MaxLatency {
					result.MaxLatency = latency
				}

				// 更新监控指标
				http3Metrics.WithLabelValues("retransmission_latency").Set(latency.Seconds())
			}
		}()
	}

	wg.Wait()
	duration := time.Since(start)

	// 计算吞吐量和丢包率
	result.Throughput = float64(result.SuccessfulRequests*int64(config.PayloadSize)) / duration.Seconds() / 1024 / 1024
	result.PacketLossRate = float64(result.FailedRequests) / float64(result.TotalRequests)

	return result, nil
}

func (r *BenchmarkResult) String() string {
	avgLatency := r.TotalLatency / time.Duration(r.SuccessfulRequests)
	return fmt.Sprintf(
		"Benchmark Results:\n"+
			"Total Requests: %d\n"+
			"Successful Requests: %d\n"+
			"Failed Requests: %d\n"+
			"Average Latency: %v\n"+
			"Min Latency: %v\n"+
			"Max Latency: %v\n"+
			"Packet Loss Rate: %.2f%%\n"+
			"Throughput: %.2f MB/s\n",
		r.TotalRequests,
		r.SuccessfulRequests,
		r.FailedRequests,
		avgLatency,
		r.MinLatency,
		r.MaxLatency,
		r.PacketLossRate*100,
		r.Throughput,
	)
}

func (r *BenchmarkResult) CalculateThroughput(config *BenchmarkConfig) float64 {
	if r.Duration.Seconds() == 0 {
		return 0
	}
	payloadSize := int64(config.PayloadSize)
	return float64(r.SuccessfulRequests*payloadSize) / r.Duration.Seconds()
}
